package com.gitee.wsl.doc.prosemirror.state.command.block.list

import com.gitee.wsl.doc.prosemirror.model.content.NodeRange
import com.gitee.wsl.doc.prosemirror.model.content.ext.liftTarget
import com.gitee.wsl.doc.prosemirror.model.fragment.Fragment
import com.gitee.wsl.doc.prosemirror.model.fragment.Slice
import com.gitee.wsl.doc.prosemirror.model.node.NodeType
import com.gitee.wsl.doc.prosemirror.state.Command
import com.gitee.wsl.doc.prosemirror.state.CommandDispatch
import com.gitee.wsl.doc.prosemirror.state.EditorState
import com.gitee.wsl.doc.prosemirror.transform.ext.delete
import com.gitee.wsl.doc.prosemirror.transform.ext.lift
import com.gitee.wsl.doc.prosemirror.transform.ext.step
import com.gitee.wsl.doc.prosemirror.transform.step.ReplaceAroundStep

class LiftListItem(val itemType: NodeType): Command {
    override fun exec(
        state: EditorState,
        dispatch: CommandDispatch?,
    ): Boolean {
        val selection = state.selection
        val from = selection._from
        val to = selection._to

        // 获取块范围，检查是否在列表项内
        val range = from.blockRange(to) { node ->
            node.childCount > 0 && node.firstChild?.type == itemType
        }

        if (range == null) {
            return false
        }

        if (dispatch == null) {
            return true
        }

        // 根据当前深度判断是在父列表内还是外部列表节点
        val parentNode = from.node(range.depth - 1)
        return if (parentNode.type == itemType) {
            // 在父列表内
            liftToOuterList(state, dispatch, itemType, range)
        } else {
            // 外部列表节点
            liftOutOfList(state, dispatch, range)
        }
    }

    fun liftToOuterList(state: EditorState, dispatch: CommandDispatch?, itemType: NodeType, range: NodeRange): Boolean {
        val tr = state.tr
        val end = range.end
        val endOfList = range.to.end(range.depth)
        var range = range

        if (end < endOfList) {
            // 在提升项之后有兄弟节点，它们必须成为最后一个项的子节点
            val newItem = itemType.create(null, range.parent.copy())
            val slice = Slice(Fragment.from(newItem), 1, 0)
            tr.step(ReplaceAroundStep(end - 1, endOfList, end, endOfList, slice, 1, true))

            // 更新范围
            val newFrom = tr.doc.resolve(range.from.pos)
            val newTo = tr.doc.resolve(endOfList)
            range = NodeRange(newFrom, newTo, range.depth)
        }

        val liftTarget = range.liftTarget()?:return false
        dispatch?.invoke(tr.lift(range, liftTarget).scrollIntoView())
        return true
    }

    fun liftOutOfList(state: EditorState, dispatch: CommandDispatch?, range: NodeRange): Boolean {
        val tr = state.tr
        val list = range.parent

        // 将列表项合并为单个大项
        var currentPos = range.end
        for (i in range.endIndex - 1 downTo range.startIndex + 1) {
            val childSize = list.child(i).nodeSize
            currentPos -= childSize
            tr.delete(currentPos - 1, currentPos + 1)
        }

        val startPos = tr.doc.resolve(range.start)
        val item = startPos.nodeAfter ?: return false
        val atStart = range.startIndex == 0
        val atEnd = range.endIndex == list.childCount

        val parent = startPos.nodeOrNull(-1) ?: return false
        val indexBefore = startPos.index(-1)

        val replacementContent = if (atEnd) {
            item.content
        } else {
            item.content.append(Fragment.from(list))
        }

        if (!parent.canReplace(indexBefore + if (atStart) 0 else 1, indexBefore + 1, replacementContent)) {
            return false
        }

        val start = startPos.pos
        val endPos = start + item.nodeSize

        // 剥离周围的列表
        val leftContent = if (atStart) Fragment.empty else Fragment.from(list.copy(Fragment.empty))
        val rightContent = if (atEnd) Fragment.empty else Fragment.from(list.copy(Fragment.empty))
        val sliceContent = leftContent.append(rightContent)
        val slice = Slice(sliceContent, if (atStart) 0 else 1, if (atEnd) 0 else 1)

        tr.step(ReplaceAroundStep(
            start - if (atStart) 1 else 0,
            endPos + if (atEnd) 1 else 0,
            start + 1,
            endPos - 1,
            slice,
            if (atStart) 0 else 1
        ))

        dispatch?.invoke(tr.scrollIntoView())
        return true
    }
}